<script setup lang="ts">
const emit = defineEmits(['handlerMove', 'update:value', 'passCallback'])

interface PropsType {
  value: boolean
  width?: number
  height?: number
  text?: string
  successText?: string
  background?: string
  progressBarBg?: string
  completedBg?: string
  circle?: boolean
  radius?: string
  handlerIcon?: string
  successIcon?: string
  handlerBg?: string
  textSize?: string
  textColor?: string
}

const props = withDefaults(defineProps<PropsType>(), {
  value: false,
  width: 260,
  height: 38,
  text: '按住滑块拖动',
  successText: 'success',
  background: '#eee',
  progressBarBg: '#1385FF',
  completedBg: '#57D187',
  circle: false,
  radius: 'calc(var(--custom-radius) / 3 + 2px)',
  handlerIcon: '&#xea50;',
  successIcon: '&#xe621;',
  handlerBg: '#fff',
  textSize: '13px',
  textColor: '#333'
})

interface stateType {
  isMoving: boolean
  x: number
  isOk: boolean
}

const state = reactive(<stateType>{
  isMoving: false,
  x: 0,
  isOk: false
})
const {isOk} = toRefs(state)
const dragVerify = ref()
const messageRef = ref()
const handler = ref()
const progressBar = ref()

// 禁止横向滑动
let startX: number, startY: number, moveX: number, moveY: number

const onTouchStart = (e: any) => {
  startX = e.targetTouches[0].pageX
  startY = e.targetTouches[0].pageY
}

const onTouchMove = (e: any) => {
  moveX = e.targetTouches[0].pageX
  moveY = e.targetTouches[0].pageY

  if (Math.abs(moveX - startX) > Math.abs(moveY - startY)) {
    e.preventDefault()
  }
}

document.addEventListener('touchstart', onTouchStart)
document.addEventListener('touchmove', onTouchMove, {passive: false})

onMounted(() => {
  dragVerify.value?.style.setProperty('--textColor', props.textColor)
  dragVerify.value?.style.setProperty('--width', Math.floor(props.width / 2) + 'px')
  dragVerify.value?.style.setProperty('--pwidth', -Math.floor(props.width / 2) + 'px')

  document.addEventListener('touchstart', onTouchStart)
  document.addEventListener('touchmove', onTouchMove, {passive: false})
})

onBeforeUnmount(() => {
  document.removeEventListener('touchstart', onTouchStart)
  document.removeEventListener('touchmove', onTouchMove)
})
const handlerStyle = {
  left: '0',
  width: props.height + 'px',
  height: props.height + 'px',
  background: props.handlerBg
}
const dragVerifyStyle = {
  width: props.width + 'px',
  height: props.height + 'px',
  lineHeight: props.height + 'px',
  background: props.background,
  borderRadius: props.circle ? props.height / 2 + 'px' : props.radius
}
const progressBarStyle = {
  background: props.progressBarBg,
  height: props.height + 'px',
  borderRadius: props.circle
    ? props.height / 2 + 'px 0 0 ' + props.height / 2 + 'px'
    : props.radius
}
const textStyle = {
  height: props.height + 'px',
  width: props.width + 'px',
  fontSize: props.textSize
}
const message = computed(() => {
  return props.value ? props.successText : props.text
})

const dragStart = (e: any) => {
  if (!props.value) {
    state.isMoving = true
    handler.value.style.transition = 'none'
    state.x =
      (e.pageX || e.touches[0].pageX) - parseInt(handler.value.style.left.replace('px', ''), 10)
  }
  emit('handlerMove')
}
const dragMoving = (e: any) => {
  if (state.isMoving && !props.value) {
    let _x = (e.pageX || e.touches[0].pageX) - state.x
    if (_x > 0 && _x <= props.width - props.height) {
      handler.value.style.left = _x + 'px'
      progressBar.value.style.width = _x + props.height / 2 + 'px'
    } else if (_x > props.width - props.height) {
      handler.value.style.left = props.width - props.height + 'px'
      progressBar.value.style.width = props.width - props.height / 2 + 'px'
      passVerify()
    }
  }
}
const dragFinish = (e: any) => {
  if (state.isMoving && !props.value) {
    let _x = (e.pageX || e.changedTouches[0].pageX) - state.x
    if (_x < props.width - props.height) {
      state.isOk = true
      handler.value.style.left = '0'
      handler.value.style.transition = 'all 0.2s'
      progressBar.value.style.width = '0'
      state.isOk = false
    } else {
      handler.value.style.transition = 'none'
      handler.value.style.left = props.width - props.height + 'px'
      progressBar.value.style.width = props.width - props.height / 2 + 'px'
      passVerify()
    }
    state.isMoving = false
  }
}
const passVerify = () => {
  emit('update:value', true)
  state.isMoving = false
  progressBar.value.style.background = props.completedBg
  messageRef.value.style['-webkit-text-fill-color'] = 'unset'
  messageRef.value.style.animation = 'slidetounlock2 3s infinite'
  messageRef.value.style.color = '#fff'
  emit('passCallback')
}
const reset = () => {
  handler.value.style.left = '0'
  progressBar.value.style.width = '0'
  handler.value.children[0].innerHTML = props.handlerIcon
  messageRef.value.style['-webkit-text-fill-color'] = 'transparent'
  messageRef.value.style.animation = 'slidetounlock 3s infinite'
  messageRef.value.style.color = props.background
  emit('update:value', false)
  state.isOk = false
  state.isMoving = false
  state.x = 0
}
defineExpose({
  reset
})
</script>

<template>
  <div
    ref="dragVerify"
    class="drag_verify"
    :style="dragVerifyStyle"
    @mousemove="dragMoving"
    @mouseup="dragFinish"
    @mouseleave="dragFinish"
    @touchmove="dragMoving"
    @touchend="dragFinish"
  >
    <div
      class="dv_progress_bar"
      :class="{ goFirst2: isOk }"
      ref="progressBar"
      :style="progressBarStyle"
    >
    </div>
    <div class="dv_text" :style="textStyle" ref="messageRef">
      <slot name="textBefore" v-if="$slots.textBefore"></slot>
      {{ message }}
      <slot name="textAfter" v-if="$slots.textAfter"></slot>
    </div>

    <div
      class="dv_handler dv_handler_bg"
      :class="{ goFirst: isOk }"
      @mousedown="dragStart"
      @touchstart="dragStart"
      ref="handler"
      :style="handlerStyle"
    >
      <i class="iconfont-sys" v-html="value ? successIcon : handlerIcon"></i>
    </div>
  </div>
</template>

<style scoped lang="scss">
.drag_verify {
  position: relative;
  overflow: hidden;
  text-align: center;
  border: 1px solid var(--art-border-dashed-color);

  .dv_handler {
    position: absolute;
    top: 0;
    left: 0;
    cursor: move;

    i {
      padding-left: 0;
      font-size: 14px;
      color: #999;
    }

    .el-icon-circle-check {
      margin-top: 9px;
      color: #6c6;
    }
  }

  .dv_progress_bar {
    position: absolute;
    width: 0;
    height: 34px;
  }

  .dv_text {
    position: absolute;
    top: 0;
    color: transparent;
    user-select: none;
    background: linear-gradient(
        to right,
        var(--textColor) 0%,
        var(--textColor) 40%,
        #fff 50%,
        var(--textColor) 60%,
        var(--textColor) 100%
    );
    background-clip: text;
    animation: slidetounlock 3s infinite;
    -webkit-text-fill-color: transparent;
    text-size-adjust: none;

    * {
      -webkit-text-fill-color: var(--textColor);
    }
  }
}

.goFirst {
  left: 0 !important;
  transition: left 0.5s;
}

.goFirst2 {
  width: 0 !important;
  transition: width 0.5s;
}
</style>

<style lang="scss">
@keyframes slidetounlock {
  0% {
    background-position: var(--pwidth) 0;
  }

  100% {
    background-position: var(--width) 0;
  }
}

@keyframes slidetounlock2 {
  0% {
    background-position: var(--pwidth) 0;
  }

  100% {
    background-position: var(--pwidth) 0;
  }
}
</style>
